import info.gridworld.grid.AbstractGrid;
import info.gridworld.grid.Location;

import java.util.ArrayList;

public class SparseBoundedGrid<E> extends AbstractGrid<E> {

    private SparseGridNode[] occArray;
    private int nRows;
    private int nCols;

    // Constructer
    public SparseBoundedGrid(int rows, int cols) {
        if (rows <= 0) {
            throw new IllegalArgumentException("Error! Rows <= 0");
        }
        if (cols <= 0) {
            throw new IllegalArgumentException("Error! Cols <= 0");
        }
        nRows = rows;
        nCols = cols;
        // the size of sparse array is rows
        occArray = new SparseGridNode[rows];
    }

    // get method for Class
    public int getNumRows() {
        return nRows;
    }

    public int getNumCols() {
        return nCols;
    }

    // Judge the location is vaild
    public boolean isValid(Location loc) {
        int r = loc.getRow();
        int c = loc.getCol();
        if ((r >= 0) && (r < getNumRows()) && (c >= 0) && (c <= getNumCols())) {
            return true;
        }
        return false;
    }

    // return the location of that the number of grid occupant
    // return_type: ArrayList<Location>
    public ArrayList<Location> getOccupiedLocations() {
        ArrayList<Location> locs = new ArrayList<Location>();

        for (int i = 0; i < getNumRows(); i++) {
            SparseGridNode pNode = occArray[i];
            while (pNode != null) {
                Location temp = new Location(i, pNode.getColumn());
                locs.add(temp);
                pNode = pNode.getNext();
            }
            // go to the next row if pNode = null
        }
        return locs;
    }

    // return_type : E (usually Actor)
    public E get(Location loc) {
        if (!isValid(loc)) {
            throw new IllegalArgumentException("Error! Location: " + loc + " is InValid.");
        }

        SparseGridNode pNode = occArray[loc.getRow()];
        // traverse nodes
        while (pNode != null) {
            if (loc.getCol() == pNode.getColumn()) {
                return (E) pNode.getOccupant();
            }
            pNode = pNode.getNext();
        }
        // return null if pNode = null
        return null;
    }

    // put the new occupant to the location
    // remove the old grid occupant
    // return the old occupant
    public E put(Location loc, E obj) {
        if (!isValid(loc)) {
            throw new IllegalArgumentException("Error! Location: " + loc + " is InValid.");
        }
        if (obj == null) {
            throw new IllegalArgumentException("Error! obj == null.");
        }

        E old = remove(loc);

        int r = loc.getRow();
        int c = loc.getCol();

        SparseGridNode pNode = occArray[r];

        occArray[r] = new SparseGridNode(obj, c, pNode);
        return old;
    }

    // remove the old occupant, and set the location to null
    // return the old occupant
    public E remove(Location loc) {
        if (!isValid(loc)) {
            throw new IllegalArgumentException("Error! Location: " + loc + " is InValid.");
        }

        E obj = get(loc);
        if (obj == null) {
            return null;
        }

        SparseGridNode pNode = occArray[loc.getRow()];

        while (pNode != null) {
            if (pNode.getColumn() == loc.getCol()) {
                occArray[loc.getRow()] = pNode.getNext();
            }
            pNode = pNode.getNext();
        }
        return obj;
    }
}
